#pragma once

#include <iostream>
#include <string>
#include <json/json.h>
#include <fcntl.h>
#include <atomic>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include "tools.hpp"

enum ErrorNo
{
    OK=0,
    PRAM_ERROR,
    INTERNAL_ERROR,
    COMPILE_ERROR,
    RUN_ERROR
};

class Compiler
{
    public:
    static void CompileAndRun(Json::Value& req,Json::Value* resp)
    {
        //1.传入的参数有错误,json串中的code是否为空
        if(req["code"].empty())
        {
            (*resp)["errorno"]=PRAM_ERROR;
            (*resp)["reason"]="pram error";
            return;
        }
        //2.将代码写到文件中去
        std::string code=req["code"].asString();
        std::string file_name=WriteTempFile(code);
        if(file_name=="")
        {
           (*resp)["errorno"] =INTERNAL_ERROR;
           (*resp)["reason"]="write file failed";
           return;
        }

        //3.编译
        if(!Compile(file_name))
        {
            (*resp)["errorno"]=COMPILE_ERROR;
            std::string reason;
            FileUtil::ReadFile(ErrorFilePath(file_name),&reason);
            (*resp)["reason"]=reason;
            return;
        }

        //4.运行
        int ret_sig=0;
        if(!Run(file_name,&ret_sig))
        {
            (*resp)["errorno"]=RUN_ERROR;
            if(ret_sig!=0)
                (*resp)["reason"]="program exit by sig:"+std::to_string(ret_sig);
            return;
        }

        //5.构造响应
        
        (*resp)["errorno"]=OK;
        (*resp)["reason"]="Compile and Run ok";
        std::string std_out;
        FileUtil::ReadFile(stdoutFilePath(file_name).c_str(),&std_out);
        (*resp)["stdout"]=std_out;
        std::string std_err;
        FileUtil::ReadFile(stderrorFilePath(file_name).c_str(),&std_err);
        (*resp)["stderr"]=std_err;

        //6.清除临时文件
        Clean(file_name); 
        return;
    }

    private:
    static void Clean(const std::string& filename)
    {
        unlink(filePath(filename).c_str());
        unlink(ExeFilePath(filename).c_str());
        unlink(ErrorFilePath(filename).c_str());
        unlink(stdoutFilePath(filename).c_str());
        unlink(stderrorFilePath(filename).c_str());
    }

    static bool Run(const std::string& filename,int* sig)
    {
        //创建子进程来运行程序
        int pid=fork();
        if(pid>0)
        {
            int status=0;
            waitpid(pid,&status,0);
            (*sig)=status & 0x7f;
        }
        else if(pid==0)
        {
            //限制运行时间
            alarm(1);

            //限制使用内存大小
            struct rlimit rlim;
            rlim.rlim_cur=30000*1024;
            rlim.rlim_max=RLIM_INFINITY;
            setrlimit(RLIMIT_AS,&rlim);


            int stdout_fd=open(stdoutFilePath(filename).c_str(),O_CREAT | O_WRONLY,0666);
            if(stdout_fd<0)
                return false;
            dup2(stdout_fd,1);

            int stdErr_fd=open(stderrorFilePath(filename).c_str(),O_CREAT | O_WRONLY,0666);
            if(stdErr_fd<0)
                return false;
            dup2(stdErr_fd,2);
            execl(ExeFilePath(filename).c_str(),ExeFilePath(filename).c_str(),NULL);
            exit(0);
        }
        else
        {
            return false;
        }
        return true;
    }

    static bool Compile(const std::string& filename)
    {
        //1.创建子进程
        //    父进程进行进程等待
        //    子进程进行进程程序替换
        int pid=fork();
        if(pid>0)
        {
            //父进程
            waitpid(pid,NULL,0);
        }
        else if(pid==0)
        {
            //子进程
            //创建错误信息要重定向的文件
            int fd=open(ErrorFilePath(filename).c_str(),O_CREAT | O_WRONLY,0666); 
            if(fd<0)
                return false;
            //将标准错误(2)重定向为fd，标准错误的输出就会输出在文件当中
            dup2(fd,2);
            execlp("g++","g++",filePath(filename).c_str(),"-o",ExeFilePath(filename).c_str(),"-std=c++11","-D","CompileOnline",NULL);
            close(fd);
            exit(0);
        }
        else
        {
            return false;
        }
        struct stat st;
        int ret=stat(ExeFilePath(filename).c_str(),&st);
        if(ret<0)
            return false;
        return true;
    }
    
    //运行时产生错误，标准错误重定向的文件路径
    static std::string stderrorFilePath(const std::string& filename)
    {
        return "./tmp_file/"+filename+".stderror";
    }

    //运行成功，标准输出重定向的文件路径
    static std::string stdoutFilePath(const std::string& filename)
    {
        return "./tmp_file/"+filename+".stdout";
    }

    //编译错误保存的文件路径
    static std::string ErrorFilePath(const std::string& filename)
    {
        return "./tmp_file/"+filename+".error";
    }

    //获取编译命令生成的文件路径
    static std::string ExeFilePath(const std::string& filename)
    {
        return "./tmp_file/"+filename+".exe";
    }

    //返回合成的保存的文件路径
    static std::string filePath(const std::string& filename)
    {
        return "./tmp_file/"+filename+".cpp";
    }

    static std::string WriteTempFile(const std::string& code)
    {
        //1.组织文件名称，区分源码文件，以及后面生成的可执行文件
        static std::atomic_uint id(0);
        std::string tmp_filename="tmp_"+std::to_string(TimeUtil::TimeStampMs())+
                "."+to_string(id);
        id++;
        FileUtil::WriteFile(filePath(tmp_filename),code);
        return tmp_filename;
    }
};
